/**
 * 
 */
package com.gragra.nounIdentification;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import com.gragra.data.Corpus;
import com.gragra.sampling.sequentialStructure.ExtensionInformationHolder;
import com.gragra.sampling.sequentialStructure.ExtensionLevels;
import com.gragra.sampling.sequentialStructure.WordAndTagHolder;
/**
 * @author Christoph Teichmann
 * 07.06.2013
 */
public class ExtendingNounIdentifier extends NounIdentifier
{
	/**
	 * 
	 */
	private Int2IntOpenHashMap counts;
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#evaluate(com.gragra.data.Corpus, boolean)
	 */
	@Override
	public double[] evaluate(Corpus<WordAndTagHolder> data, boolean microAverage)
	{
		counts = (new Int2IntOpenHashMap());
		double seen = 0;
		for(WordAndTagHolder wth : data)
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			super.getSg().fillMappedSequence(wth);
			for(int i=0;i<wth.getLength();++i)
			{
				increaseCount(wth, eih, i);
				++seen;
			}
		}
		reduceCounts(seen);
		return super.evaluate(data, microAverage);
	}
	/**
	 * @param seen 
	 * 
	 */
	protected void reduceCounts(double seen)
	{this.reduceCounter(getCounts(),seen);}
	/**
	 * @param wth
	 * @param eih
	 * @param position
	 */
	protected void increaseCount(WordAndTagHolder wth,
			ExtensionInformationHolder eih, int position)
	{
		int tag;
		if((tag = eih.getInfiniteInformation(position, ExtensionLevels.ContextTagLevel)) >= 0 && super.decideTypePresent(wth, position))
		{getCounts().addTo(tag, 1);}
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#decideTypePresent(com.gragra.sampling.sequentialStructure.WordAndTagHolder, int)
	 */
	@Override
	protected boolean decideTypePresent(WordAndTagHolder wth, int j)
	{return getCounts().containsKey(wth.getExtensionInformation().getInfiniteInformation(j, ExtensionLevels.ContextTagLevel)) || super.decideTypePresent(wth, j);}
	/**
	 * @param data
	 * @param frameSize
	 * @param preMarkers
	 * @param postMarkers
	 * @param sco
	 * @param ca
	 * @param ma
	 * @param minCount
	 */
	public ExtendingNounIdentifier(Corpus<WordAndTagHolder> data,
			int frameSize, int preMarkers, int postMarkers, Scorer sco,
			ConvergenceAssessment ca, MaxAssessment ma, int upto)
	{
		super(data, frameSize, preMarkers, postMarkers, sco, ca, ma);
		number = upto;
	}
	/**
	 * 
	 */
	private final int number;
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#annotateCorpusFrames(com.gragra.data.Corpus)
	 */
	@Override
	public void annotateCorpusFrames(Corpus<WordAndTagHolder> data)
	{
		super.annotateCorpusFrames(data);
		extendNouns(data);
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.BaseNounIdentifier#annotateCorpusWords(com.gragra.data.Corpus)
	 */
	@Override
	public void annotateCorpusWords(Corpus<WordAndTagHolder> data)
	{
		super.annotateCorpusWords(data);
		extendNouns(data);
	}
	/**
	 * @param data
	 */
	protected void extendNouns(Corpus<WordAndTagHolder> data)
	{
		Int2IntOpenHashMap map = new Int2IntOpenHashMap();
		double seen = 0.0;
		for(WordAndTagHolder wth : data)
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			super.getSg().fillMappedSequence(wth);
			for(int i=0;i<wth.getLength();++i)
			{
				extendMap(map, eih, i,wth);
				++seen;
			}
		}
		reduceCounter(map,seen);
		for(WordAndTagHolder wth : data)
		{
			ExtensionInformationHolder eih = wth.getExtensionInformation();
			super.getSg().fillMappedSequence(wth);
			for(int i=0;i<wth.getLength();++i)
			{
				if(testForAdding(map, wth, eih, i))
				{eih.addInformation(i, ExtensionLevels.WordTypeLevel, this.getAssociatedType());}
			}
		}
	}
	/**
	 * @param map
	 * @param wth
	 * @param eih
	 * @param i
	 * @return
	 */
	protected boolean testForAdding(Int2IntOpenHashMap map,
			WordAndTagHolder wth, ExtensionInformationHolder eih, int i)
	{return map.containsKey(eih.getInfiniteInformation(i, ExtensionLevels.ContextTagLevel))|| super.decideTypePresent(wth, i);}
	/**
	 * @param map
	 * @param seen 
	 */
	protected void reduceCounter(Int2IntOpenHashMap map,double irrelevant)
	{
		Int2ObjectAVLTreeMap<IntSet> list = new Int2ObjectAVLTreeMap<IntSet>();
		IntIterator iit = map.keySet().iterator();
		while(iit.hasNext())
		{
			int i = iit.nextInt();
			int val = -map.get(i);
			IntSet is = list.get(val);
			if(is == null)
			{
				is = new IntOpenHashSet();
				list.put(val, is);
			}
			is.add(i);
		}
		int count = 0;
		map.clear();
		iit = list.keySet().iterator();
		while(count < this.number)
		{
			IntIterator internal = list.get(iit.nextInt()).iterator();
			while(count < this.number && internal.hasNext())
			{
				int c = internal.nextInt();
				map.put(c, ++count);
			}
		}
	}
	/**
	 * @param map
	 * @param eih
	 * @param i
	 */
	protected void extendMap(Int2IntOpenHashMap map,
			ExtensionInformationHolder eih, int i, WordAndTagHolder wth)
	{
		int tag;
		if(eih.getFiniteInformation(i, ExtensionLevels.WordTypeLevel) == this.getAssociatedType() && (tag = eih.getInfiniteInformation(i, ExtensionLevels.ContextTagLevel)) >=  0)
		{map.addTo(tag, 1);}
	}
	/**
	 * 
	 * @return
	 */
	public Int2IntOpenHashMap getCounts()
	{return counts;}
}